#include "ioc.hpp"
#include <stack>

namespace IoC
{
    // thread_local
    std::stack<Container*> containers;

    Container::Container()
    {
        containers.emplace(this);
        RegisterSingletonUnManagement(this);
    }

    Container::~Container()
    {
        containers.pop();
        lock_guard<decltype(m_mtx_)> lck(m_mtx_);

        // auto const this_type = GetTypeId<Container>();

        for (auto &c1: m_types_)
        {
            // if (c1.first == this_type)
                // continue;
            for (auto &c2: c1.second)
            {
                delete c2.second;
            }
        }
    }

    void Container::Add(tid_t type, const std::string& key, details::BaseGetter* getter)
    {
        lock_guard<decltype(m_mtx_)> lck(m_mtx_);

        if (getter == nullptr)
        {
            throw invalid_argument("bad getter!!");
        }

        if (m_types_.find(type) == end(m_types_))
        {
            m_types_.insert(pair<tid_t, GetterMap>{type, {}});
        }

        auto& con = m_types_.at(type);
        if (con.find(key) != end(con))
            throw invalid_argument("key already exists!!");

        con.emplace(key, getter);
    }

    void Container::Remove(tid_t type, const std::string& key)
    {
        lock_guard<decltype(m_mtx_)> lck(m_mtx_);
        if (m_types_.find(type) != end(m_types_))
        {
            auto& con = m_types_.at(type);

            auto it = con.find(key);
            if (it != end(con))
            {
                delete it->second;
                con.erase(it);
            }
        }
    }

    Container* Container::Current()
    {
        if (containers.empty())
            return nullptr;
        return containers.top();
    }
}
